<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Generator函数的语法</title>
  <style>
    body {
      color: #333;
      line-height: 40px;
    }
    span {
      color: red;
    }
  </style>
</head>
<body>
  <h2>简介</h2>
  <ul>
    <li>1.Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同.此处只介绍语法,不介绍异步应用</li>
    
    <li>2.从语法上理解,可以理解为一个状态机,封装了多个内部状态</li>

    <li>3.执行Generator函数会返回一个遍历器对象.也就是说Generator<span>除了是状态机,还是一个遍历器对象生成函数</span>.返回的遍历器对象可以依次遍历Generator函数内部的每一个状态</li>

    <li>
      形式上,Generator函数是一个普通函数,但是有两个特征:
      <ul>
        <li>function与函数名字中间有个星号</li>
        <li>二是函数体内部使用yield语句定义不同的内部状态</li>
      </ul>
    </li>

    <li>
      调用Generator函数后,必须调用遍历器对象的next()方法,使得指针移向下一个状态.直到遇到下一条yield语句(或者return语句)为止,换言之,<span>Generator函数是分段执行的,yield语句是暂停执行的标记,而next()方法可以恢复执行</span>
    </li>
  </ul>

  <h2>yield表达式</h2>
  <h3>遍历器对象的next方法的运行逻辑如下</h3>
  <ul>
    <li>
      1.遇到yield语句就暂停执行后面的操作,并将紧跟在yield后的表达式的值作为返回的对象的value值
    </li>
    <li>
      2.下一次调用next方法时再继续往下执行,直到遇到下一个yield为止
    </li>
    <li>
      3.如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值作为返回对象的value属性值
    </li>
    <li>
      4.如果该函数没有return语句,则返回对象的value属性值为undefined
    </li>
    <li>
      Generator函数可以称之为""<span>生成器</span>",另外yield表达式只能用在Generator函数里面,用在其他地方都会报错
    </li>
    <li>
      yield如果用在另一个表达式之中,必须放在圆括号里面
    </li>
  </ul>

  <h2>与Iterator接口的关系</h2>
  <ul>
    <li>
      由于Generator函数就是遍历器生成函数，因此可以把Generator赋值给对象的Symbol.iterator属性，从而使得该对象具有Iterator接口
    </li>
    <li>
      Generator函数执行后，返回一个遍历器对象。该对象本身也具有Symbol.iterator属性，执行后返回自身
    </li>
  </ul>

  <h2>next方法的参数</h2>
  <ul>
    <li>
      yield语句本身没有返回值，或者说总是返回undefined.next方法可以带有一个参数，该参数会被当作上一条yield语句的返回值
    </li>
    <li>
      这个参数具有很重要的语法意义。可以在Generator函数运行的不同阶段从外部向内部注入不同的值
    </li>
    <li>
      由于next方法的参数表示上一条yield语句的返回值，所以第一次使用next方法时传递参数时无效的，只有从第二次使用next方法开始，参数才是有效的
    </li>
  </ul>

  <h2>for...of...循环</h2>
  <ul>
    <li>
      for...of...循环可以自动遍历Generator函数生成的Iterator对象，且此时不再需要调用next方法,一旦next方法的返回对象的done属性为true，for...of循环就会终止，<span>且不包含该返回对象</span>
    </li>
    <li>
      利用for循环，可以写出遍历任何对象的方法。原生的Javascript对象没有遍历接口，无法使用for...of循环，通过Generator函数为它加上这个接口后可使得对象可遍历
    </li>
    <li>
      除了for...of循环，扩展运算符...、解构赋值和Array.from方法内部调用的都是遍历器接口。这意味着，<span>他们都可以将Generaor函数返回的Iterator对象作为参数</span>
    </li>
  </ul>

  <h2>Generator.prototype.throw()</h2>
  <ul>
    <li>
      Generator函数返回的遍历器都有一个throw方法，<span>可以在函数体外抛出错误，然后在Generator函数体内捕获</span>
    </li>
    <li>
      throw方法接受一个参数，该参数会被catch语句接收，<span>建议抛出Error对象的实例</span>
    </li>
    <li>
      不要混淆遍历器对象的throw方法和全局的throw命令。上面的错误是用遍历器对象的throw方法抛出的，而不是用全局throw命令抛出的，后者只能被函数体外的catch语句捕获
    </li>
    <li>
      如果Generator函数内部部署了try...catch代码块，那么遍历器的throw方法抛出的错误将不影响下一次遍历，否则遍历直接终止<span>throw方法被捕获以后会附带执行下一条yield表达式，即附带执行一次next方法</span>
    </li>
  </ul>

  <h2>Generator.prototype.return()</h2>
  <ul>
    <li>Generator函数返回的遍历器对象还有一个return方法，可以返回给定的值，并且终结Generator遍历</li>
    <li>
      如果Generator函数内部有try...finally代码块，那么return方法会推迟到finally代码块执行完再执行
    </li>
  </ul>

  <h2>yield*表达式</h2>
  <div>
    用作在一个Generator函数里面执行另一个Generator函数
  </div>
  <ul>
    <li>
      Generator函数不能跟new运算符一起使用，否则就会报错
    </li>
  </ul>

  <h2>Generator与协程</h2>
  <ul>
    <li>
      多个函数可以并行执行，但是只有一个函数正在运行，其他函数都属于暂停状态
    </li>
    <li>
      协程种函数之间可以交换执行权，<span>也就是说，一个函数执行到一半，可以暂停执行，将执行权交给另一个函数，等到收回执行权的时候在恢复执行</span>
    </li>
    <li>
      协程是以多占用内存为代价实现多任务的并行运行
    </li>
    <li>
      Generator函数是ES6对协程的实现，但是属于不完全实现。Generator函数称为“半协程”<span>意思是只有Generator函数的调用者才能将程序的执行权还给Generator函数</span>。如果是完全实现的协程，任何函数都可以让暂停的协程继续执行
    </li>
    <li>
      异步操作的同步化表达：意味着可以把异步操作写在yield语句里面，等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了
    </li>
  </ul>


  <script src="./node.js"></script>
</body>
</html>